/*
 *
 *   Copyright (C) 2003-2005 by Raymond Huang
 *   plushuang at users.sourceforge.net
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 2.1 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library; if not, write to the Free Software
 *  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 *
 *  ---
 *
 *  In addition, as a special exception, the copyright holders give
 *  permission to link the code of portions of this program with the
 *  OpenSSL library under certain conditions as described in each
 *  individual source file, and distribute linked combinations
 *  including the two.
 *  You must obey the GNU Lesser General Public License in all respects
 *  for all of the code used other than OpenSSL.  If you modify
 *  file(s) with this exception, you may extend this exception to your
 *  version of the file(s), but you are not obligated to do so.  If you
 *  do not wish to do so, delete this exception statement from your
 *  version.  If you delete this exception statement from all source
 *  files in the program, then also delete it here.
 *
 */

/*
 *  conf_parser.c
 *      Parse configuration from XML files.
 */

#include <stdio.h>
#include <string.h>
#include <urlglib/urlglib_util.h>
#include <urlglib/conf_parser.h>

static const GMarkupParser empty_conf_parser = {
	NULL, NULL, NULL, NULL, NULL
};

ConfParser* conf_parser_new ()
{
	ConfParser* cp = g_malloc0 (sizeof (ConfParser));

	conf_parser_init (cp);
	return cp;
}

void conf_parser_destroy (ConfParser* cp)
{
	conf_parser_finalize (cp);
	g_free (cp);
}

void conf_parser_init (ConfParser* cp)
{
	cp->parse_context = g_markup_parse_context_new (&cp->parser, 0, cp, NULL);
	cp->stack = g_ptr_array_sized_new (16);  // 8 level XML
	cp->data = NULL;
}

void conf_parser_finalize (ConfParser* cp)
{
	g_markup_parse_context_free (cp->parse_context);
	g_ptr_array_free (cp->stack, TRUE);
}

// conf_parser_push ()
//
// If GMarkupParseContext can switch parser when parsing, it will speed up.
// I need function like g_markup_parse_context_set_parser () or g_markup_parse_context_push_parser ().
//
void conf_parser_push (ConfParser* cp, const GMarkupParser* parser, void* data)
{
	g_ptr_array_add (cp->stack, (gpointer)parser);
	g_ptr_array_add (cp->stack, data);
	// switch parser and it's data ...
	memcpy (&cp->parser, parser, sizeof (GMarkupParser));
	cp->data = data;
}

void conf_parser_pop (ConfParser* cp)
{
	GPtrArray* array = cp->stack;
	const GMarkupParser* parser;

	if (array->len <= 2) {
		// avoid crash if XML file correct.
		array->len = 0;
		parser = &empty_conf_parser;
		cp->data = NULL;
	} else {
		array->len -= 2;
		parser = g_ptr_array_index (array, array->len - 2);
		cp->data = g_ptr_array_index (array, array->len - 1);
	}
	memcpy (&cp->parser, parser, sizeof (GMarkupParser));
}

void conf_parser_push_data (ConfParser*  cp, gpointer  data)
{
	GPtrArray* array = cp->stack;
	GMarkupParser* parser;

	// return if no parser exist
	if (array->len < 2)
		return;

	parser = g_ptr_array_index (array, array->len-2);
	g_ptr_array_add (array, parser);
	g_ptr_array_add (array, data);
	cp->data = data;
}

void conf_parser_pop_data (ConfParser*  cp)
{
	GPtrArray* array = cp->stack;

	if (array->len <= 2) {
		// avoid crash if XML file correct.
		array->len  = 0;
		memcpy (&cp->parser, &empty_conf_parser, sizeof (GMarkupParser));
		cp->data = NULL;
	} else {
		array->len -= 2;
		cp->data = g_ptr_array_index (array, array->len - 1);
	}
}

gboolean conf_parser_read_file (ConfParser* cp,
                                const gchar* filename,
                                GError** error)
{
	FILE*    file;
	guint8*  buffer;
	guint    size;
	gboolean parse_ok = TRUE;

	file = ug_fopen (filename, "r");
	if (file==NULL)
		return FALSE;

	buffer = g_malloc (4096);

	while ( (size=fread (buffer, 1, 4096, file)) && parse_ok) {
		parse_ok = g_markup_parse_context_parse (cp->parse_context,
		                                         buffer, size, error);
	}

	g_markup_parse_context_end_parse (cp->parse_context, NULL);
	g_free (buffer);
	fclose (file);

	return parse_ok;
}

void conf_parser_text_default (GMarkupParseContext*  gmpc,
                               const gchar*          text,
                               gsize                 text_len,
                               gpointer              cparser,
                               GError**              error)
{
	char** data = (char**) CONF_PARSER_DATA (cparser);

	str_replace (data, text, text_len);
}

void conf_parser_text_no_crlf (GMarkupParseContext*  gmpc,
                               const gchar*          text,
                               gsize                 text_len,
                               gpointer              cparser,
                               GError**              error)
{
	char** data = (char**) CONF_PARSER_DATA (cparser);

	str_replace_no_crlf (data, text, text_len);
}

void conf_parser_push_text_func (ConfParser* cparser, MarkupParserTextFunc func, gchar** dest_string)
{
	conf_parser_set_text_func (cparser, func);
	conf_parser_push_data (cparser, (gpointer)dest_string);
}

void conf_parser_push_text_func_defalut (ConfParser* cparser, gchar** dest_string)
{
	conf_parser_set_text_func (cparser, conf_parser_text_default);
	conf_parser_push_data (cparser, (gpointer)dest_string);
}

void conf_parser_push_text_func_no_crlf (ConfParser* cparser, gchar** dest_string)
{
	conf_parser_set_text_func (cparser, conf_parser_text_no_crlf);
	conf_parser_push_data (cparser, (gpointer)dest_string);
}

void conf_parser_pop_text_func (ConfParser* cparser)
{
	conf_parser_set_text_func (cparser, NULL);
	conf_parser_pop_data (cparser);
}

